#include "assignment3.h"
#include "init.h"
#include <math.h>
#include <stdio.h>
#include <GL/glut.h>

#define INCREMENT 0.0025	// used in making lines for the condensation set
#define PI 3.1415926535		// used for rotations

vector<Matrix> fractalTransforms;
//vector<Pt> fractal;		// the points in the fractal
vector<Pt> conSet;		// the condensation set
int depth;


Matrix translate ( Vec v )
{
	Matrix rvalue, trans;
	
	// Translation matrix is essentially I with v in the third column
	// Identity matrix
	rvalue.data[0][0] = 1;
	rvalue.data[1][1] = 1;
	rvalue.data[2][2] = 1;
	
	// put in translation
	rvalue.data[0][2] = v.x;
	rvalue.data[1][2] = v.y;
	
	return rvalue;
}


Matrix rotate ( Pt p, float theta )	// p = o
{
	Matrix rvalue;

	rvalue.data[2][2] = 1;
	
	rvalue.data[0][0] = cos(theta);
	rvalue.data[0][1] = -1*sin(theta);
	rvalue.data[1][0] = sin(theta);
	rvalue.data[1][1] = cos(theta);
	rvalue.data[0][2] = p.x + p.y*sin(theta) - p.x*cos(theta);
	rvalue.data[1][2] = p.y - p.y*cos(theta) - p.x*sin(theta);
	
	return rvalue;
}


Matrix scale ( Pt p, float alpha )
{
	Matrix rvalue;
	
	rvalue.data[2][2] = 1;
	
	rvalue.data[0][0] = alpha;
	rvalue.data[1][1] = alpha;

	rvalue.data[0][2] = (1-alpha)*p.x;
	rvalue.data[1][2] = (1-alpha)*p.y;	

	return rvalue;
}


Matrix nscale ( Pt p, Vec v, float alpha )
{
	Matrix rvalue;

	rvalue.data[2][2] = 1;	
	
	rvalue.data[0][0] = 1+(alpha-1)*v.x*v.x;
	rvalue.data[1][1] = 1+(alpha-1)*v.y*v.y;
	
	rvalue.data[1][0] = (alpha-1)*v.x*v.y;
	rvalue.data[0][1] = (alpha-1)*v.x*v.y;
	
	rvalue.data[0][2] = v.x*(p.x*v.x+p.y*v.y)*(1-alpha);
	rvalue.data[1][2] = v.y*(p.x*v.x+p.y*v.y)*(1-alpha);
	
	return rvalue;
}


Matrix image ( Pt p1, Pt p2, Pt p3, Pt q1, Pt q2, Pt q3 )
{
	Matrix rvalue;
	Matrix pInverse, pTranspose;
	Matrix q;
	
	// rvalue = [q1 q2 q3]*[p1 p2 p3]^-1
	
	// Matrix inverse formula taken from Wikipedia
	float det = p1.x*(p2.y-p3.y) + p2.x*(p3.y-p1.y) + p3.x*(p1.y-p2.y);
	if(det == 0)
		printf("%f\n",det);


	float A = p2.y-p3.y;
	float B = p3.y-p1.y;
	float C = p1.y-p2.y;
	float D = p3.x-p2.x;
	float E = p1.x-p3.x;
	float F = p2.x-p1.x;
	float G = p2.x*p3.y - p3.x*p2.y;
	float H = p3.x*p1.y - p1.x*p3.y;
	float K = p1.x*p2.y - p2.x*p1.y;
	
	pInverse.data[0][0] = (1/det)*A;
	pInverse.data[1][0] = (1/det)*B;	
	pInverse.data[2][0] = (1/det)*C;	
	pInverse.data[0][1] = (1/det)*D;
	pInverse.data[1][1] = (1/det)*E;	
	pInverse.data[2][1] = (1/det)*F;
	pInverse.data[0][2] = (1/det)*G;
	pInverse.data[1][2] = (1/det)*H;
	pInverse.data[2][2] = (1/det)*K;


	q.data[0][0] = q1.x;
	q.data[1][0] = q1.y;
	q.data[2][0] = 1;
	q.data[0][1] = q2.x;
	q.data[1][1] = q2.y;
	q.data[2][1] = 1;
	q.data[0][2] = q3.x;
	q.data[1][2] = q3.y;
	q.data[2][2] = 1;


	rvalue = compose(q,pInverse);
	int i,j;
	for ( i = 0; i < 3; i++ )
	{
		for ( j = 0; j < 3; j++ )
		{
			printf(" %f ",rvalue.data[i][j]);
		}
		printf("\n");
	}

	return rvalue;
}


Matrix compose ( Matrix m2, Matrix m1 )
{
	Matrix rvalue;
	
	rvalue.data[0][0] = m2.data[0][0]*m1.data[0][0] + m2.data[0][1]*m1.data[1][0] + m2.data[0][2]*m1.data[2][0];
	rvalue.data[0][1] = m2.data[0][0]*m1.data[0][1] + m2.data[0][1]*m1.data[1][1] + m2.data[0][2]*m1.data[2][1];
	rvalue.data[0][2] = m2.data[0][0]*m1.data[0][2] + m2.data[0][1]*m1.data[1][2] + m2.data[0][2]*m1.data[2][2];
	
	rvalue.data[1][0] = m2.data[1][0]*m1.data[0][0] + m2.data[1][1]*m1.data[1][0] + m2.data[1][2]*m1.data[2][0];
	rvalue.data[1][1] = m2.data[1][0]*m1.data[0][1] + m2.data[1][1]*m1.data[1][1] + m2.data[1][2]*m1.data[2][1];
	rvalue.data[1][2] = m2.data[1][0]*m1.data[0][2] + m2.data[1][1]*m1.data[1][2] + m2.data[1][2]*m1.data[2][2];
	
	rvalue.data[2][0] = m2.data[2][0]*m1.data[0][0] + m2.data[2][1]*m1.data[1][0] + m2.data[2][2]*m1.data[2][0];
	rvalue.data[2][1] = m2.data[2][0]*m1.data[0][1] + m2.data[2][1]*m1.data[1][1] + m2.data[2][2]*m1.data[2][1];
	rvalue.data[2][2] = m2.data[2][0]*m1.data[0][2] + m2.data[2][1]*m1.data[1][2] + m2.data[2][2]*m1.data[2][2];	
	
	return rvalue;
}


// apply the transform to p, using the math from homogeneous 
//   coordinates (though p is still a 2x1)
Pt transform ( Matrix m, Pt p )
{
	Pt q;
	q.x = m.data[0][0]*p.x + m.data[0][1]*p.y + m.data[0][2];
	q.y = m.data[1][0]*p.x + m.data[1][1]*p.y + m.data[1][2];

//	if((m.data[2][0] != 0) || (m.data[2][1] != 0) || (m.data[2][2] != 1))
//		printf("\nSomething's wrong with the transform, Cap'n!\n");

	return q;
}


void lineToPoints(Pt p1, Pt p2)
{
	if(p1.x > p2.x)	// this way p1 is always to the left
	{
		Pt temp = p2;
		p2 = p1;
		p1 = temp;
	}

	if(p1.x != p2.x)	// not vertical
	{
		float slope = (p2.y-p1.y)/(p2.x-p1.x);
		float currentY = p1.y;
//		int roundY;

		for(float currentX = p1.x; currentX < p2.x; currentX += INCREMENT)
		{
//			roundY = (int)currentY + 1;
			
//			if(((double)roundY - currentY) >= 0.5)	// currentY decimal is < 1/2
//				roundY = (int)currentY;

//			conSet.push_back(Pt(currentX, roundY));
			conSet.push_back(Pt(currentX, currentY));
			currentY += slope*INCREMENT;
		}
	}

	else	// vertical
	{
		if(p1.y > p2.y)	// this way p1 is always below p2
		{
			Pt temp = p2;
			p2 = p1;
			p1 = temp;
		}

		for(float currentY = p1.y; currentY < p2.y; currentY += INCREMENT)
			conSet.push_back(Pt(p1.x, currentY));
	}
}


// sets the depth for the fractal's drawing
void setDepth(int _depth)
{
	depth = _depth;
}


// still need to finish  use the vector conSet
void setCondensationSet ( vector<Pt> pts )
{
	conSet.clear();

	vector<Pt>::iterator pIter, pIter2;
	pIter = pts.begin();
	pIter2 = pts.begin();


	switch (pts.size()) {
		case 0:
			;	// no condensation set			
			break;
		case 1:
			// point
			conSet.push_back(*pIter);
			break;
		case 2:
			// line
			pIter2++;
			lineToPoints((*pIter),(*pIter2));
			break;
		default:
			if (pts.size() > 2) {	// polyline
				pIter2++;
				while(pIter2 != pts.end())
				{	// will add to conSet
					lineToPoints((*pIter),(*pIter2));
					pIter++;
					pIter2++;
				}
			}
			break;
	}
}


// store the list of transformations to use in drawing the fractal
void setIATTransformations ( vector<Matrix> transformations )
{
	fractalTransforms.clear();
	vector<Matrix>::iterator mIter;
	for(mIter = transformations.begin(); mIter != transformations.end(); mIter++)
		fractalTransforms.push_back((*mIter));
}



void keyboard(unsigned char key, int x, int y)
{
	glColor3f(1,1,1);

	vector<Pt> condensation;
	vector<Matrix> transforms;

	switch(key) {
	case '1':	// Fractal Hangman
		printf("Fractal Hangman\n");
		setDepth(10);

		transforms.push_back(scale(Pt(-0.5,-0.5),0.5));
		transforms.push_back(scale(Pt( 0.5,-0.5),0.5));
		transforms.push_back(compose(scale(Pt(0.5,0.5),0.5),rotate(Pt(0,0),PI/2)));
		
		// the transforms below were sampled from the image on the website; simply comment out the above transforms
		//    and uncomment the below ones to see the fractal hangman from the online image			
/*
		transforms.push_back(image(Pt((4.0-235.0/2)/235,(-223.0+230.0/2)/230),Pt((223.0-235.0/2)/235,(-223.0+230.0/2)/230),
			Pt((223.0-235.0/2)/235,(-4.0+230.0/2)/230),
			Pt((5.0-235.0/2)/235,(-223.0+230.0/2)/230),
			Pt((114.0-235.0/2)/235,(-223.0+230.0/2)/230),Pt((114.0-235.0/2)/235,(-114.0+230.0/2)/230)));
		transforms.push_back(image(Pt((4.0-235.0/2)/235,(-223.0+230.0/2)/230),Pt((223.0-235.0/2)/235,(-223.0+230.0/2)/230),
			Pt((223.0-235.0/2)/235,(-4.0+230.0/2)/230),
			Pt((114.0-235.0/2)/235,(-223.0+230.0/2)/230),
			Pt((223.0-235.0/2)/235,(-223.0+230.0/2)/230),Pt((223.0-235.0/2)/235,(-114.0+230.0/2)/230)));
		transforms.push_back(image(Pt((4.0-235.0/2)/235,(-223.0+230.0/2)/230),Pt((223.0-235.0/2)/235,(-223.0+230.0/2)/230),
			Pt((223.0-235.0/2)/235,(-4.0+230.0/2)/230),
			Pt((223.0-235.0/2)/235,(-114.0+230.0/2)/230),
			Pt((223.0-235.0/2)/235,(-4.0+230.0/2)/230),Pt((114.0-235.0/2)/235,(-4.0+230.0/2)/230)));
*/		
		break;

			
	case '2':	// Fractal Staircase
		printf("Fractal Staircase\n");
		setDepth(10);

		condensation.push_back(Pt(-0.25, -0.25));
		condensation.push_back(Pt( 0.25, -0.25));
		condensation.push_back(Pt( 0.25,  0.25));
		condensation.push_back(Pt(-0.25,  0.25));
		condensation.push_back(Pt(-0.25, -0.25));

		transforms.push_back(scale(Pt(0.75,-0.25),0.5));
		transforms.push_back(scale(Pt(-0.25,0.75),0.5));
		
		
		// the transforms below were sampled from the image on the website; simply comment out the above transforms
		//    and uncomment the below ones to see the fractal staircase from the online image
/*
		condensation.push_back(Pt((7.0-236.0/2)/236,(-224.0+231.0/2)/231));
		condensation.push_back(Pt((117.0-236.0/2)/236,(-224.0+231.0/2)/231));
		condensation.push_back(Pt((117.0-236.0/2)/236,(-114.0+231.0/2)/231));
		condensation.push_back(Pt((7.0-236.0/2)/236,(-114.0+231.0/2)/231));
		condensation.push_back(Pt((7.0-236.0/2)/236,(-224.0+231.0/2)/231));

		transforms.push_back(image(Pt((7.0-236.0/2)/236,(-224.0+231.0/2)/231),Pt((117.0-236.0/2)/236,(-224.0+231.0/2)/231),
			Pt((7.0-236.0/2)/236,(-114.0+231.0/2)/231),Pt((7.0-236.0/2)/236,(-114.0+231.0/2)/231),
			Pt((62.0-236.0/2)/236,(-114.0+231.0/2)/231),Pt((7.0-236.0/2)/236,(-59.0+231.0/2)/231)));

		transforms.push_back(image(Pt((7.0-236.0/2)/236,(-224.0+231.0/2)/231),Pt((117.0-236.0/2)/236,(-224.0+231.0/2)/231),
			Pt((7.0-236.0/2)/236,(-114.0+231.0/2)/231),Pt((117.0-236.0/2)/236,(-224.0+231.0/2)/231),
			Pt((172.0-236.0/2)/236,(-224.0+231.0/2)/231),Pt((117.0-236.0/2)/236,(-169.0+231.0/2)/231)));
*/
		break;


	case '3':	// Fractal Snowflake
		printf("Fractal Snowflake\n");
		setDepth(6);
			
		transforms.push_back(image(Pt((58.0-255.0/2)/255,(-218.0+231.0/2)/231),Pt((194.0-255.0/2)/255,(-218.0+231.0/2)/231),
			Pt((236.0-255.0/2)/255,(-89.0+231.0/2)/231),
			Pt((58.0-255.0/2)/255,(-218.0+231.0/2)/231),
			Pt((109.0-255.0/2)/255,(-218.0+231.0/2)/231),Pt((125.0-255.0/2)/255,(-169.0+231.0/2)/231)));
		transforms.push_back(image(Pt((58.0-255.0/2)/255,(-218.0+231.0/2)/231),Pt((194.0-255.0/2)/255,(-218.0+231.0/2)/231),
			Pt((236.0-255.0/2)/255,(-89.0+231.0/2)/231),
			Pt((143.0-255.0/2)/255,(-218.0+231.0/2)/231),
			Pt((194.0-255.0/2)/255,(-218.0+231.0/2)/231),Pt((210.0-255.0/2)/255,(-169.0+231.0/2)/231)));
		transforms.push_back(image(Pt((58.0-255.0/2)/255,(-218.0+231.0/2)/231),Pt((194.0-255.0/2)/255,(-218.0+231.0/2)/231),
			Pt((236.0-255.0/2)/255,(-89.0+231.0/2)/231),
			Pt((168.0-255.0/2)/255,(-138.0+231.0/2)/231),
			Pt((219.0-255.0/2)/255,(-138.0+231.0/2)/231),Pt((235.0-255.0/2)/255,(-89.0+231.0/2)/231)));
		transforms.push_back(image(Pt((58.0-255.0/2)/255,(-218.0+231.0/2)/231),Pt((194.0-255.0/2)/255,(-218.0+231.0/2)/231),
			Pt((236.0-255.0/2)/255,(-89.0+231.0/2)/231),
			Pt((100.0-255.0/2)/255,(-88.0+231.0/2)/231),
			Pt((152.0-255.0/2)/255,(-88.0+231.0/2)/231),Pt((168.0-255.0/2)/255,(-39.0+231.0/2)/231)));
		transforms.push_back(image(Pt((58.0-255.0/2)/255,(-218.0+231.0/2)/231),Pt((194.0-255.0/2)/255,(-218.0+231.0/2)/231),
			Pt((236.0-255.0/2)/255,(-89.0+231.0/2)/231),
			Pt((32.0-255.0/2)/255,(-138.0+231.0/2)/231),
			Pt((84.0-255.0/2)/255,(-138.0+231.0/2)/231),Pt((100.0-255.0/2)/255,(-89.0+231.0/2)/231)));
		transforms.push_back(image(Pt((58.0-255.0/2)/255,(-218.0+231.0/2)/231),Pt((194.0-255.0/2)/255,(-218.0+231.0/2)/231),
			Pt((236.0-255.0/2)/255,(-89.0+231.0/2)/231),
			Pt((152.0-255.0/2)/255,(-88.0+231.0/2)/231),
			Pt((103.0-255.0/2)/255,(-88.0+231.0/2)/231),Pt((86.0-255.0/2)/255,(-138.0+231.0/2)/231)));
		break;

			
	case '4':	// Hex Flower
		printf("Hex Flower\n");
		setDepth(6);

		// this set of transforms will also produce a similar hex flower
		transforms.push_back(scale(Pt(-0.33333,-0.66667),0.33333));
		transforms.push_back(scale(Pt(-0.33333,0.66667),0.33333));
		transforms.push_back(scale(Pt(0.33333,-0.66667),0.33333));
		transforms.push_back(scale(Pt(0.33333,0.66667),0.33333));
		transforms.push_back(scale(Pt(-0.66667,0),0.33333));
		transforms.push_back(scale(Pt(0.66667,0),0.33333));

			
		// the transforms below were sampled from the image on the website; simply comment out the above transforms
		//    and uncomment the below ones to see the hex flower from the online image
/*		
		transforms.push_back(image(Pt((60.0-231.0/2)/231,(208.0-229.0/2)/229),Pt((170.0-231.0/2)/231,(208.0-229.0/2)/229),
			Pt((60.0-231.0/2)/231,(19.0-229.0/2)/229),Pt((60.0-231.0/2)/231,(208.0-229.0/2)/229),
			Pt((97.0-231.0/2)/231,(208.0-229.0/2)/229),Pt((60.0-231.0/2)/231,(146.0-229.0/2)/229)));

		transforms.push_back(image(Pt((60.0-231.0/2)/231,(208.0-229.0/2)/229),Pt((170.0-231.0/2)/231,(208.0-229.0/2)/229),
			Pt((60.0-231.0/2)/231,(19.0-229.0/2)/229),Pt((133.0-231.0/2)/231,(208.0-229.0/2)/229),
			Pt((170.0-231.0/2)/231,(208.0-229.0/2)/229),Pt((133.0-231.0/2)/231,(146.0-229.0/2)/229)));

		transforms.push_back(image(Pt((60.0-231.0/2)/231,(208.0-229.0/2)/229),Pt((170.0-231.0/2)/231,(208.0-229.0/2)/229),
			Pt((60.0-231.0/2)/231,(19.0-229.0/2)/229),Pt((170.0-231.0/2)/231,(145.0-229.0/2)/229),
			Pt((207.0-231.0/2)/231,(145.0-229.0/2)/229),Pt((170.0-231.0/2)/231,(82.0-229.0/2)/229)));

		transforms.push_back(image(Pt((60.0-231.0/2)/231,(208.0-229.0/2)/229),Pt((170.0-231.0/2)/231,(208.0-229.0/2)/229),
			Pt((60.0-231.0/2)/231,(19.0-229.0/2)/229),Pt((133.0-231.0/2)/231,(81.0-229.0/2)/229),
			Pt((170.0-231.0/2)/231,(81.0-229.0/2)/229),Pt((133.0-231.0/2)/231,(19.0-229.0/2)/229)));

		transforms.push_back(image(Pt((60.0-231.0/2)/231,(208.0-229.0/2)/229),Pt((170.0-231.0/2)/231,(208.0-229.0/2)/229),
			Pt((60.0-231.0/2)/231,(19.0-229.0/2)/229),Pt((60.0-231.0/2)/231,(81.0-229.0/2)/229),
			Pt((97.0-231.0/2)/231,(81.0-229.0/2)/229),Pt((60.0-231.0/2)/231,(19.0-229.0/2)/229)));

		transforms.push_back(image(Pt((60.0-231.0/2)/231,(208.0-229.0/2)/229),Pt((170.0-231.0/2)/231,(208.0-229.0/2)/229),
			Pt((60.0-231.0/2)/231,(19.0-229.0/2)/229),Pt((23.0-231.0/2)/231,(145.0-229.0/2)/229),
			Pt((60.0-231.0/2)/231,(145.0-229.0/2)/229),Pt((24.0-231.0/2)/231,(82.0-229.0/2)/229)));
*/
		break;

			
	case '5':	// Personal fractal 1
		printf("Personal Fractal 1\n");
		setDepth(4);
		glColor3f(1,0,0);

		double x,y,r;
		r = 0.5;		
		x = 0.5;
		y = 0;
		for(double theta = -PI/2; theta < PI/2; theta += 0.05)
			condensation.push_back(Pt(x+r*cos(theta),y+r*sin(theta)));
		transforms.push_back(compose(rotate(Pt(x,y),PI/6),scale(Pt(x,y),0.33)));		


		x = 0;
		y = 0.5;
		for(double theta = 0.0; theta < PI; theta += 0.05)
			condensation.push_back(Pt(x+r*cos(theta),y+r*sin(theta)));
		transforms.push_back(compose(rotate(Pt(x,y),PI/6),scale(Pt(x,y),0.33)));

		x = -0.5;
		y = 0;
		for(double theta = PI/2; theta < 3*PI/2; theta += 0.05)
			condensation.push_back(Pt(x+r*cos(theta),y+r*sin(theta)));
		transforms.push_back(compose(rotate(Pt(x,y),PI/6),scale(Pt(x,y),0.33)));	

		x = 0;
		y = -0.5;
		for(double theta = PI; theta < 2*PI; theta += 0.05)
			condensation.push_back(Pt(x+r*cos(theta),y+r*sin(theta)));
		transforms.push_back(compose(rotate(Pt(x,y),PI/6),scale(Pt(x,y),0.33)));	
			
			
		transforms.push_back(compose(rotate(Pt(0,0),PI/6),scale(Pt(0,0),0.175)));

		break;

			
	case '6':	// Personal fractal 2
		printf("Personal Fractal 2\n");
		setDepth(8);

		condensation.push_back(Pt(-0.5,0));
		condensation.push_back(Pt(0.5,0));

		transforms.push_back(compose(compose(compose(nscale(Pt(0,0),Vec(0.5,0.5),0.75),rotate(Pt(0,0),4*PI/25)),
			scale(Pt(0,-1),0.5)),rotate(Pt(0,0), PI/3)));
		transforms.push_back(compose(compose(compose(nscale(Pt(0,0),Vec(0.5,0.5),0.75),rotate(Pt(0,0),4*PI/25)),
			scale(Pt(0, 1),0.5)),rotate(Pt(0,0), PI/3)));

		break;

	default:
		break;
	}

	setCondensationSet(condensation);
	setIATTransformations( transforms );
	glutPostRedisplay();
}



// Draws the current IAT
void display ( void )
{
	glClear ( GL_COLOR_BUFFER_BIT );

	Pt temp;	
	vector<Pt>::iterator pIter;
	vector<Pt> tempFractal;
	vector<Pt> fractal;
	vector<Matrix>::iterator mIter;
	
	// since the attractor is converged to regardless of the starting shape, choose
	//   an arbitrary point, (0,0)  (unless there is a condensation set)
	if(conSet.empty())
		fractal.push_back(Pt(0,0));

	// if there is a condensation set, include that, too
	for(pIter = conSet.begin(); pIter != conSet.end(); pIter++)
		fractal.push_back((*pIter));

	// go through depths of transforms
	for(int i = 0; i < depth; i++)
	{
		for(pIter = fractal.begin(); pIter != fractal.end(); pIter++)
		{
			temp = (*pIter);
			for(mIter = fractalTransforms.begin(); mIter != fractalTransforms.end(); mIter++)
			{
				// push back the modified point
				tempFractal.push_back(transform((*mIter), temp));
			}
		}
		fractal = tempFractal;
		tempFractal.clear();

		// at each depth of traversal, add the condensation set back in
		for(pIter = conSet.begin(); pIter != conSet.end(); pIter++)
		{
			fractal.push_back((*pIter));
		}	
	}

	glBegin ( GL_POINTS );
		// for each point in the fractal's final list, draw it
		for(pIter = fractal.begin(); pIter != fractal.end(); pIter++)
		{
			glVertex2f( (*pIter).x,(*pIter).y );
		}
	glEnd();
	glFlush ( );
}


/* do not modify the reshape function */
void reshape ( int width, int height )
{
	glViewport ( 0, 0, width, height );
	glMatrixMode ( GL_PROJECTION );
	glLoadIdentity ( );    
	gluOrtho2D (-1, 1, -1, 1);
	glMatrixMode ( GL_MODELVIEW );
    glLoadIdentity ( );
}

int main ( int argc, char** argv )
{
	setDepth(6);	// default

	glutInit ( &argc, argv );
	glutInitDisplayMode ( GLUT_SINGLE | GLUT_RGB );
	glutInitWindowSize ( 800, 800 );
	glutInitWindowPosition ( 100, 100 );
	glutCreateWindow ( "John Lake - Homework 3" );
	init ( );	
	glutDisplayFunc ( display );
	glutReshapeFunc ( reshape );
	glutKeyboardFunc ( keyboard );
	glutMainLoop ( );
	return 0;
}

